#include "timer_engine.hpp"

#include <chrono>
#include <thread>
#include <mutex>
#include <atomic>
#include <vector>
#include <sys/prctl.h>

#include "timer.hpp"

namespace DcAdr
{

    TimerEngine g_global_timer_engine;

    std::int64_t TimerEngine::GetCurrentMillisecs()
    {
        auto duration = std::chrono::steady_clock::now().time_since_epoch();
        return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    }

    // ---------------------------------------------------------

    template <typename Mutex>
    class OptionLockGuard
    {
    public:
        OptionLockGuard(Mutex &mutex, bool lock)
            : mutex_(mutex), lock_(lock)
        {
            if (lock_)
            {
                mutex_.lock();
            }
        }
        ~OptionLockGuard()
        {
            if (lock_)
            {
                mutex_.unlock();
            }
        }

    private:
        Mutex &mutex_;
        bool lock_;
    };

    // ------------------------------------------------------------

    class TimerEnginePrivate
    {
    public:
        using HeapNode = Timer *;
        using Heap = std::vector<HeapNode>;
        class MinHeapHelper;

    public:
        TimerEnginePrivate();
        ~TimerEnginePrivate();

        void AddTimer(Timer *timer, bool lock);
        void RemoveTimer(Timer *timer, bool lock);

    private:
        void DetectTimers();
        void ThreadFunc();

    public:
        Heap heap_;
        std::mutex heap_mutex_;

    private:
        std::thread th_;
        std::atomic_bool thread_running_{true};
    };

    // ------------------------------------------------------------

    // 最小堆辅助类
    class TimerEnginePrivate::MinHeapHelper
    {
    public:
        static void SwapNode(TimerEnginePrivate::Heap &heap, std::size_t index1, std::size_t index2);
        static void AdjustUp(TimerEnginePrivate::Heap &heap, std::size_t index);
        static void AdjustDown(TimerEnginePrivate::Heap &heap, std::size_t index);
    };

    void TimerEnginePrivate::MinHeapHelper::SwapNode(TimerEnginePrivate::Heap &heap, std::size_t index1, std::size_t index2)
    {
        heap[index1]->GetIdRef() = index2;
        heap[index2]->GetIdRef() = index1;
        std::swap(heap[index1], heap[index2]);
    }

    void TimerEnginePrivate::MinHeapHelper::AdjustUp(TimerEnginePrivate::Heap &heap, std::size_t index)
    {
        std::size_t parent = (index - 1) / 2;
        while (index > 0 && heap[index]->GetExpires() < heap[parent]->GetExpires())
        {
            SwapNode(heap, index, parent);
            index = parent;
            parent = (index - 1) / 2;
        }
    }

    void TimerEnginePrivate::MinHeapHelper::AdjustDown(TimerEnginePrivate::Heap &heap, std::size_t index)
    {
        std::size_t child = index * 2 + 1;
        std::size_t min_child = 0;
        while (child < heap.size())
        {
            if (child + 1 == heap.size() || heap[child]->GetExpires() < heap[child + 1]->GetExpires())
            {
                min_child = child;
            }
            else
            {
                min_child = child + 1;
            }

            if (heap[index]->GetExpires() < heap[min_child]->GetExpires())
            {
                break;
            }

            SwapNode(heap, index, min_child);
            index = min_child;
            child = index * 2 + 1;
        }
    }

    // ---------------------------------------------------------

    TimerEnginePrivate::TimerEnginePrivate()
    {
        th_ = std::thread(&TimerEnginePrivate::ThreadFunc, this);
    }

    TimerEnginePrivate::~TimerEnginePrivate()
    {
        thread_running_ = false;
        if (th_.joinable())
        {
            th_.join();
        }
    }

    void TimerEnginePrivate::DetectTimers()
    {
        std::int64_t time_now = TimerEngine::GetCurrentMillisecs();

        OptionLockGuard<std::mutex> lock(heap_mutex_, true);
        while (!heap_.empty() && heap_[0]->GetExpires() <= time_now)
        {
            auto timer = heap_[0];
            RemoveTimer(timer, false);
            timer->OnTimeout(time_now);
        }
    }

    void TimerEnginePrivate::ThreadFunc()
    {
        prctl(PR_SET_NAME, "dc_timer");
        while (thread_running_)
        {
            DetectTimers();
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
    }

    void TimerEnginePrivate::AddTimer(Timer *timer, bool need_lock)
    {
        OptionLockGuard<std::mutex> lock(heap_mutex_, need_lock);

        timer->GetIdRef() = heap_.size();
        heap_.push_back(timer);
        MinHeapHelper::AdjustUp(heap_, heap_.size() - 1);
    }

    void TimerEnginePrivate::RemoveTimer(Timer *timer, bool need_lock)
    {
        OptionLockGuard<std::mutex> lock(heap_mutex_, need_lock);

        std::size_t index = timer->GetIdRef();
        if (!heap_.empty() && index < heap_.size())
        {
            if (index == heap_.size() - 1)
            {
                heap_.pop_back();
            }
            else
            {
                MinHeapHelper::SwapNode(heap_, index, heap_.size() - 1);
                heap_.pop_back();
                size_t parent = (index - 1) / 2;
                if (index > 0 && heap_[index]->GetExpires() < heap_[parent]->GetExpires())
                {
                    MinHeapHelper::AdjustUp(heap_, index);
                }
                else
                {
                    MinHeapHelper::AdjustDown(heap_, index);
                }
            }
        }
    }

    // ------------------------------------------------------------

    TimerEngine::TimerEngine()
        : pri_(new TimerEnginePrivate())
    {
    }

    TimerEngine::~TimerEngine()
    {
    }

    void TimerEngine::AddTimer(Timer *timer, bool lock)
    {
        pri_->AddTimer(timer, lock);
    }

    void TimerEngine::RemoveTimer(Timer *timer, bool lock)
    {
        pri_->RemoveTimer(timer, lock);
    }

} // namespace DcAdr